/*
  Copyright 1996-2003
  Simon Whiteside

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

* $Id: ExecutableRoot.java,v 1.28 2003/12/18 14:41:23 sdw Exp $
*/

package simkin;
import java.util.Hashtable;

/**
 * This class provides an implementation of the Executable interface which can 
 * be conveniently extended
 * <p>The class implements some commonly used methods via the {@link simkin.ExecutableRoot#method method} function:<ul>
 * <li>the method "trace" which outputs a message to a tracer.
 * <li>the method "isObject" which returns true if the argument is an object
 * <li>the method "length" which returns the length of a string
 * <li>the method "charAt" which returns a character within a string </ul>
 * <li>the method "toString" which coerces the parameter to a string </ul>
 * <li>the method "toInt" which coerces the parameter to an integer </ul>
 * <li>the method "toFloat" which coerces the parameter to a floating point value </ul>
 * <li>the method "toChar" which coerces the parameter to a character </ul>
 * <li>the method "toBool" which coerces the parameter to a boolean </ul>
 * <p>It also implements all the methods of the {@link simkin.Executable Executable} interface which means that derived classes need only implement the methods they need to override.
 * <p>The class uses the Interpreter's reflection methods to look for public fields and method names which match those being accessed by the Simkin script. 
 */
public class ExecutableRoot implements Executable {
    /**
     * this is the name of the trace method which this class exposes
     */
    static final String s_trace="trace";
    /**
     * this is the name of the isObject method which this class exposes
     */
    static final String s_isObject="isObject";
    /**
     * this is the name of the length method which this class exposes
     */
    static final String s_length="length";
    /**
     * this is the name of the charAt method which this class exposes
     */
    static final String s_charAt="charAt";
    /**
     * this is the name of the toString method which this class exposes
     */
    static final String s_toString="toString";
    /**
     * this is the name of the toInt method which this class exposes
     */
    static final String s_toInt="toInt";
    /**
     * this is the name of the toFloat method which this class exposes
     */
    static final String s_toFloat="toFloat";
    /**
     * this is the name of the toBool method which this class exposes
     */
    static final String s_toBool="toBool";
    /**
     * this is the name of the toChar method which this class exposes
     */
    static final String s_toChar="toChar";
    /**
     * This method calls the Interpreter's reflectiveSetValue method
     */
    public void setValue(String field_name,String attrib,Object value,ExecutableContext ctxt)  throws RuntimeException,FieldNotSupportedException{
	ctxt.getInterpreter().reflectiveSetValue(ctxt,this,field_name,value);
    }
    /**
     * This method does nothing at this level
     */
    public void setValueAt(Object array_index,String attrib,Object value,ExecutableContext ctxt) throws RuntimeException{
    }
    /**
     * This method calls the Interpreter's reflectiveGetValue method
     */
    public Object getValue(String field_name,String attrib, ExecutableContext ctxt) throws RuntimeException,FieldNotSupportedException{
	return ctxt.getInterpreter().reflectiveGetValue(ctxt,this,field_name);
    }
    /**
     * This method does nothing at this level
     */
    public Object getValueAt(Object array_index,String attrib, ExecutableContext ctxt) throws RuntimeException{
	return null;
    }
    /**
     * The following methods are supported directly by this class:
     * <p> 
     * <p>trace(a) - sends a message to the trace output, takes a variable number of arguments
     * <p>isObject(a) - returns true if a is an object (as opposed to an integer or string)
     * <p>length(a) - returns the length of a (taken as a string)
     * <p>charAt(str,index) - returns the character at the given index within the string
     * <p>If the method is not one of these, the Interpreter's reflectiveMethodCall method is called
     * @return true if the method is found
     */
    public Object method(String method_name,Object[] arguments, ExecutableContext ctxt) throws ParseException, RuntimeException,MethodNotSupportedException{
	Object ret=null;
        if (method_name.equals(s_toString) && arguments!=null){
	    ret=arguments[0].toString();
        }else if (method_name.equals(s_toInt) && arguments!=null){
	    ret=new Integer(Interpreter.intValue(arguments[0]));
            //#ifndef no_float
        }else if (method_name.equals(s_toFloat) && arguments!=null){
	    ret=new Float(Interpreter.floatValue(arguments[0]));
            //#endif
        }else if (method_name.equals(s_toChar) && arguments!=null){
	    ret=new Character(arguments[0].toString().charAt(0));
            //#ifndef midp
        }else if (method_name.equals(s_toBool) && arguments!=null){
            if (Interpreter.boolValue(arguments[0]))
                ret=Boolean.TRUE;
            else
                ret=Boolean.FALSE;
            //#endif
        }else if (method_name.equals(s_trace) && arguments!=null){
	    StringBuffer buffer=new StringBuffer();
	    for (int i=0;i<arguments.length;i++)
		buffer.append(arguments[i].toString());
	    ctxt.getInterpreter().trace(buffer.toString());
	}else if (method_name.equals(s_isObject) && arguments!=null){
		Object arg=arguments[0];
		if ((arg instanceof Integer || 
                     //#ifndef no_float
                     arg instanceof Double || 
                     //#endif
                     arg instanceof Boolean || arg instanceof Character || arg instanceof String))
		    ret=Interpreter.g_False;
		else
		    ret=Interpreter.g_True;
	}else if (method_name.equals(s_length) && arguments!=null)
	    ret=new Integer(arguments[0].toString().length());
	else if (method_name.equals(s_charAt) && arguments!=null && arguments.length>1)
	    ret=new Character(arguments[0].toString().charAt(Interpreter.intValue(arguments[1])));
	else{
	    ret=ctxt.getInterpreter().reflectiveMethodCall(ctxt,this,method_name,arguments);
	}
	return ret;
    }
    /** Returns null - by default can't be iterated over
     */
    public ExecutableIterator createIterator(){
	return null;
    }
    /** Returns null - by default can't be iterated over
     */
    public ExecutableIterator createIterator(String qualifier){
	return null;
    }
    /**
     * This method returns the source for a scripted method described by the location. At this level it returns a blank string.
     */
    public String getSource(String location){
	return "";
    }
    /**
     * This method returns the instance variables for this object. At this level it returns null.
     */
    public Hashtable getInstanceVariables(){
	return null;
    }
    /**
     * This method returns the attributes for this object. At this level it returns null.
     * @return table a table filled with the attribute values. Each entry in the table maps an attribute name to a value
     */
    public Hashtable getAttributes(){
	return null;
    }
};
